#include <iostream>
using namespace std;

#include "../gzip/deflate.h"

int main()
{
	/* Endian test */
	// TODO
	/* Data-types test */
	cout << "Data-types test: ";
	if((sizeof(unsigned char ) == 1) && (sizeof(char ) == 1) &&
	   (sizeof(unsigned short) == 2) && (sizeof(short) == 2) &&
	   (sizeof(unsigned int  ) == 4) && (sizeof(int  ) == 4))
	    cout << "OK." << endl;
	else
	{
		cout << "FAIL!" << endl;
		return 1;
	}
	//
	/* Deflate distances test */
	cout << "Deflate distances test: ";
	unsigned short dval;
	unsigned int dpos = 0;
	unsigned char *dbuff = new unsigned char[32768*3];	// 3B ~ (3+15)b
	memset(dbuff, 0, 32768*3);
	for(unsigned short d = 1; d <= 32768; ++d)
		Distance_Encode(d, dbuff, dpos);
	//
	dpos = 0;
	for(unsigned short d = 1; d <= 32768; ++d)
	{
		dval = 0;
		Distance_Decode(dbuff, dpos, dval);
		if(dval != d)
		{
			cout << "FAIL! (" << dval  << " != " << d << ")" << endl;
			return 1;
		}
	}
	cout << "OK." << endl;
	delete [] dbuff;
	//
	/* Deflate literals test */
	cout << "Deflate literals test: ";
	unsigned short lival;
	unsigned int lipos = 0;
	unsigned char *libuff = new unsigned char[256*2];	// 2B ~ 9b
	memset(libuff, 0, 256*2);
	for(unsigned short li = 0; li <= 256; ++li)
		Huffman_Encode_Symbol(li, lipos, libuff);
	//
	lipos = 0;
	for(unsigned short li = 0; li <= 256; ++li)
	{
		lival = 0;
		Huffman_Decode_Symbol(libuff, lipos, lival);
		if(lival != li)
		{
			cout << "FAIL! (" << lival  << " != " << li << ")" << endl;
			return 1;
		}
	}
	cout << "OK." << endl;
	delete [] libuff;
	//
	/* Deflate lengths test */
	cout << "Deflate lengths test: ";
	unsigned short lval, lenc;
	unsigned int lpos = 0;
	unsigned char *lbuff = new unsigned char[257*2];	// 2B ~ (9+5)b
	memset(lbuff, 0, 257*2);
	for(unsigned short l = 3; l <= 257; ++l)
		Length_Encode(l, lbuff, lpos);
	//
	lpos = 0;
	for(unsigned short l = 3; l <= 257; ++l)
	{
		lval = 0;
		Huffman_Decode_Symbol(lbuff, lpos, lenc);
		Length_Decode(lenc, lbuff, lpos, lval);
		if(lval != l)
		{
			cout << "FAIL! (" << lval  << " != " << l << ")" << endl;
			return 1;
		}
	}
	cout << "OK." << endl;
	delete [] lbuff;
	//
	/* Deflate test */
    /**/
	cout << "Deflate compression/decompression test: ";
    unsigned char T[256] = "aa?baacaaaaaababab";	// this uses 7b, 8b and 9b codewords
	T[2] = 145;
	//cout << '\n' << T << endl;
    //
    // LZ77 & Huffman & Distance Encode
    unsigned int enc_bit_pos = 0;
    unsigned char encoded[1024];
    memset(encoded, 0, 1024);
    //
    vector<Triplet> triplets;
    LZ77_Encode((const unsigned char *)T, strlen((const char *)T), triplets);
    //for(size_t i = 0, im = triplets.size(); i < im; i++)
    //    cout << '(' << triplets[i].i << ',' << triplets[i].j << ',' << triplets[i].a << ") ";
    //cout << endl;
    //
	// 0 is encoded as 256, therefore I don't need to insert END_OF_BLOCK symbol
    for(size_t i = 0, im = triplets.size(); i < im; i++)
    {
        if(triplets[i].i > 0) Length_Encode(triplets[i].j, encoded, enc_bit_pos);   // encode length
        if(triplets[i].j > 0) Distance_Encode(triplets[i].i, encoded, enc_bit_pos); // distance
        Huffman_Encode_Symbol(triplets[i].a, enc_bit_pos, encoded); // literal
    }
    //
    // Huffman & Distance & LZ77 Decode
    char decoded[1024];
    memset(decoded, 0, 1024);
    int dec_pos = 0;
    unsigned int dec_bit_pos = 0;
    unsigned short lit, len, dist;
    while(dec_bit_pos <= enc_bit_pos)
    {
        Huffman_Decode_Symbol(encoded, dec_bit_pos, lit); // literal/length
        if(lit < 256)   // literal
            decoded[dec_pos++] = (unsigned char)lit;
        else if(lit == 256) // end of block
            break;
        else //if(lit > 256)    // length
        {
            Length_Decode(lit, encoded, dec_bit_pos, len);    // length
            Distance_Decode(encoded, dec_bit_pos, dist); // distance
            for(int p = dec_pos-dist; len > 0; --len, ++p)
                decoded[dec_pos++] = decoded[p];
        }
    }
    //cout << decoded << endl;
    //
    if(strcmp((const char *)T, decoded) == 0)
        cout << "OK." << endl;
    else
	{
        cout << "FAIL!" << endl;
		return 1;
	}
    //
    return 0;
    /**/
    /* Length coding test - the old one -- commented version */
    /*
    unsigned short l = 258;
    unsigned short code, extra;
    unsigned char extra_len;
    Length_Encode(l, code, extra, extra_len);
    cout << "Code=" << code << ";Extra=" << extra << ";Bits=" << int(extra_len) << endl;
    //
    unsigned short val;
    Length_Decode(code, extra, val);
    cout << val << endl;
    //
    if(l == val)
        cout << "OK." << endl;
    else
        cout << "FAIL!" << endl;
    //
    return 0;
    */
    /* Distance coding test */
    /*
    unsigned short d = 32768;
    unsigned char test[] = { 0,0,0,0,0,0,0,0,0,0,0 };
    unsigned int bit_pos = 0;
    Distance_Encode(d, test, bit_pos);
    //
    for(int i = 0, im = BitStream_Bytes(bit_pos); i < im; i++)
        cout << "0x" << hex << int(test[i]) << ' ';
    cout << endl;
    //
    unsigned short val;
    unsigned int dec_pos = 0;
    Distance_Decode(test, dec_pos, val);
    cout << dec << val << endl;
    //
    if(d == val)
        cout << "OK." << endl;
    else
        cout << "FAIL!" << endl;
    //
    return 0;
    */
    /* Huffman test */
    /*
    const char *T = "aabaacaaaaaababab";
    unsigned char encoded[1024];
    memset(encoded, 0, 1024);
    unsigned int bit_pos = 0;
    for(int i = 0, im = strlen(T); i < im; ++i)
        Huffman_Encode_Symbol(T[i], bit_pos, encoded);
    //
    for(int i = 0, im = BitStream_Bytes(bit_pos); i < im; i++)
        cout << "0x" << hex << int(encoded[i]) << ' ';
    cout << endl;
    //
    //
    unsigned int enc_bit_len = bit_pos + 1;
    bit_pos = 0;
    char decoded[256], di = 0;
    unsigned short val;
    while(bit_pos < enc_bit_len)
    {
        Huffman_Decode_Symbol(encoded, bit_pos, val);
        decoded[di++] = val;
    }
    decoded[di] = 0;
    cout << decoded << endl;
    if(strcmp(decoded, T) == 0)
        cout << "Ok." << endl;
    else
        cout << "Fail!" << endl;
    //
    return 0;
    */
    /* LZ77 test */
    /*
    const char *T = "aabaacaaaaaababab";
    vector<Triplet> triplets;
    LZ77_Encode((const unsigned char *)T, strlen(T), triplets);
    for(size_t i = 0, im = triplets.size(); i < im; i++)
        cout << '(' << triplets[i].i << ',' << triplets[i].j << ',' << triplets[i].a << ") ";
    cout << endl;
    //
    char *decoded = new char[1024];
    LZ77_Decode(triplets, (unsigned char *)decoded);
    cout << decoded << endl;
    //
    if(strcmp(T, decoded) == 0)
        cout << "OK." << endl;
    else
        cout << "FAIL!" << endl;
    //
    delete [] decoded;
    return 0;
    */
    /* BitStream_Add test */
    /*
    unsigned char test[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    unsigned int pozice = 0;
    BitStream_Add(7, 127, test, pozice);
    BitStream_Add(8, 129, test, pozice);
    BitStream_Add(9, 257, test, pozice);
    for(int i = 0, im = BitStream_Bytes(pozice); i < im; i++)
        cout << "0x" << hex << int(test[i]) << ' ';
    cout << endl;
    return 0;
    */
}